// @Title main.go
// @Description cava smu服务主函数文件
// @Author yezhibin 2021年4月29日16:50:23
// @Update

package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"syscall"
	"time"

	business "Projects/CavaCloudSys/cavasmusrc/com.business"
	qfcfg "Projects/CavaCloudSys/cavasmusrc/com.qfcfg"
	Mycontext "Projects/CavaCloudSys/cavasmusrc/internal/context"

	jsoniter "github.com/json-iterator/go"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/context"
	qflog "github.com/qf-tech/qftech-base/pkg/log"
	"github.com/qf-tech/qftech-base/pkg/qfcrypt"
	"github.com/shirou/gopsutil/host"
)

// middleWare def
func middleWare(c iris.Context) {
	start := time.Now()
	business.Controller{}.CheckTraceID(c)
	traceID := c.GetHeader("TRACE-ID")
	decoder := jsoniter.NewDecoder(c.Request().Body)
	// 解析参数 存入map
	reqParams := make(map[string]string, 0)
	_ = decoder.Decode(&reqParams)
	mJSON, _ := jsoniter.Marshal(reqParams)
	dataStr := string(mJSON)
	uri := c.Request().RequestURI
	qflog.Sugare.Infof("TraceID=%s|uri: %s method: %s, request data: %s", traceID, uri, c.Method(), dataStr)
	c.Next()
	// 统计时间
	since := time.Since(start)
	qflog.Sugare.Infof("TraceID=%s|cost: %v", traceID, since)
}

// Cors 跨域请求处理
func Cors(ctx iris.Context) {
	ctx.Header("Access-Control-Allow-Origin", "*")
	if ctx.Request().Method == "OPTIONS" {
		ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS")
		ctx.Header("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization")
		ctx.StatusCode(204)
		return
	}
	ctx.Next()
}

func newRouter() *iris.Application {
	app := iris.New()
	// app.Use(iris.Logger())
	// app.Use(app.Recovery())

	app.ContextPool.Attach(func() iris.Context {
		return &Mycontext.Context{
			// If you use the embedded Context,
			// call the `context.NewContext` to create one:
			Context: context.NewContext(app),
		}
	})

	// 注册中间件
	app.Use(Cors)
	app.Use(middleWare)

	api := app.Party("/api")
	{
		devapi := api.Party("/dev")
		{
			devapi.Get("/teu/list", business.Controller{}.GetTeuInfo)
		}
	}

	return app
}

func httpServer() {
	server := qfcfg.ServerConfig.Server
	address := fmt.Sprintf("%s:%d", server.IP, server.ApiPort)

	router := newRouter()
	err := router.Run(iris.Addr(address))
	if err != nil {
		qflog.Sugare.Errorf("listen err: %v", err)
		os.Exit(1)
	}
}

const stdErrFile = "./log/server.log"

var stdErrFileHandler *os.File

func rewriteStderrFile() error {
	if runtime.GOOS == "windows" {
		return nil
	}

	osInfo, err := host.Info()
	if err != nil {
		qflog.Sugare.Errorf("get host info err: %v", err)
		return nil
	}

	if osInfo.KernelArch != "x86_64" {
		return nil
	}

	file, err := os.OpenFile(stdErrFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println(err)
		return err
	}
	stdErrFileHandler = file //把文件句柄保存到全局变量，避免被GC回收

	if err = syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
		fmt.Println(err)
		return err
	}
	// 内存回收前关闭文件描述符
	runtime.SetFinalizer(stdErrFileHandler, func(fd *os.File) {
		fd.Close()
	})

	return nil
}

func initCrypt() {
	currentVersionFlag := "QFTECH"
	key := "lmN4dtPyeC5r29DYBLl0P0OoA4Afy/2UnCg0zd+hHhg="

	oldKeys := make(map[string][]byte)
	zeroKeytr := "q_1dY=Khec2nMNxV"
	oldKeys[qfcrypt.ZeroVersionFlag] = []byte(zeroKeytr)

	_ = qfcrypt.Init(currentVersionFlag, []byte(key), oldKeys)
}

func main() {
	config := &qflog.LogConfig{
		MaxCount: 90,
		MaxSize:  30,
		Compress: true,
		FilePath: "./log/server.log",
		Level:    qflog.InfoLevel,
		Format:   qflog.JsonType,
	}

	qflog.Init(config)
	initCrypt()
	_ = rewriteStderrFile()
	qfcfg.InitConfig()

	// 打印版本信息
	path, _ := os.Executable()
	_, svrName := filepath.Split(path)
	flag.Parse()
	if option.PrintVersion {
		fmt.Printf("%s %s %s (Git SHA: %s, Go Version: %s, BuildAt: %s)\n", svrName, GitBranch, Version, GitSHA, runtime.Version(), BuildAt)
		os.Exit(0)
	}

	var demo business.Controller
	demo.Init()

	qflog.Sugare.Infof("cava smu start.")

	http.HandleFunc("/ws", demo.WsHandle)

	go httpServer()

	address := fmt.Sprintf("%s:%d", qfcfg.ServerConfig.Server.IP, qfcfg.ServerConfig.Server.Port)
	_ = http.ListenAndServe(address, nil)
}
